using ColorFillGame.Domain.Exceptions;

namespace ColorFillGame.Domain.ValueObjects;

public class Position : ValueObject
{
    public int X { get; private set; }
    public int Y { get; private set; }

    private Position()
    {
        // EF Core constructor
    }

    public Position(int x, int y)
    {
        X = x;
        Y = y;
    }

    public bool IsValid(int gridSize)
    {
        return X >= 0 && X < gridSize && Y >= 0 && Y < gridSize;
    }

    public Position Move(Direction direction)
    {
        switch (direction)
        {
            case Direction.Up:
                return new Position(X, Y - 1);
            case Direction.Down:
                return new Position(X, Y + 1);
            case Direction.Left:
                return new Position(X - 1, Y);
            case Direction.Right:
                return new Position(X + 1, Y);
            default:
                throw new DomainException("Invalid direction.");
        }
    }

    public IEnumerable<Position> GetNeighbors()
    {
        yield return new Position(X - 1, Y); // Left
        yield return new Position(X + 1, Y); // Right
        yield return new Position(X, Y - 1); // Up
        yield return new Position(X, Y + 1); // Down
    }

    protected override IEnumerable<object> GetAtomicValues()
    {
        yield return X;
        yield return Y;
    }
}

public enum Direction
{
    Up,
    Down,
    Left,
    Right
}

public abstract class ValueObject
{
    protected abstract IEnumerable<object> GetAtomicValues();

    public override bool Equals(object? obj)
    {
        if (obj is null || obj.GetType() != GetType())
        {
            return false;
        }

        var other = (ValueObject)obj;
        
        using var thisValues = GetAtomicValues().GetEnumerator();
        using var otherValues = other.GetAtomicValues().GetEnumerator();
        
        while (thisValues.MoveNext() && otherValues.MoveNext())
        {
            if (thisValues.Current is null ^ otherValues.Current is null)
            {
                return false;
            }

            if (thisValues.Current != null &&
                !thisValues.Current.Equals(otherValues.Current))
            {
                return false;
            }
        }

        return !thisValues.MoveNext() && !otherValues.MoveNext();
    }

    public override int GetHashCode()
    {
        // 使用更稳健的哈希码计算方式，减少哈希冲突
        int hashCode = 17;
        foreach (var value in GetAtomicValues())
        {
            hashCode = hashCode * 31 + (value?.GetHashCode() ?? 0);
        }
        return hashCode;
    }

    public static bool operator ==(ValueObject? left, ValueObject? right)
    {
        if (left is null && right is null)
            return true;
        
        if (left is null || right is null)
            return false;
        
        return left.Equals(right);
    }

    public static bool operator !=(ValueObject? left, ValueObject? right)
    {
        return !(left == right);
    }
}